package com.globallogic.javatrainings.sergiiivanov.controller;

import com.globallogic.javatrainings.sergiiivanov.model.Building;
import com.globallogic.javatrainings.sergiiivanov.model.Const;
import com.globallogic.javatrainings.sergiiivanov.model.Const.Discipline;
import com.globallogic.javatrainings.sergiiivanov.model.Course;
import com.globallogic.javatrainings.sergiiivanov.model.Lecture;
import com.globallogic.javatrainings.sergiiivanov.model.Lecture.LectureType;
import com.globallogic.javatrainings.sergiiivanov.model.MyArray;
import com.globallogic.javatrainings.sergiiivanov.model.Room;
import com.globallogic.javatrainings.sergiiivanov.model.Student;
import com.globallogic.javatrainings.sergiiivanov.model.University;
import com.globallogic.javatrainings.sergiiivanov.model.UniversityFailure;
import com.globallogic.javatrainings.sergiiivanov.model.UniversityWarning;
import com.globallogic.javatrainings.sergiiivanov.resources.Messages;
import com.globallogic.javatrainings.sergiiivanov.view.CoursesManagementPage;

class CoursesMenuController extends BasicController {
    private enum States {
        SHOW_COURSES, SHOW_COURSE_DETAILS, SHOW_LECTURES, SHOW_ENROLLED_STUDENTS
    }

    private static final String GUI_COURSES_ADD_NEW_COURSE = "Add new course";;
    private CoursesManagementPage page = new CoursesManagementPage();
    private States currentState;
    private int command;// , indexSelectedCourse, indexSelectedLecture;
    private Course selectedCourse;
    private Lecture selectedLecture;
    private String selectedCourseTitle;
    private University university;

    @Override
    BasicController initiate(University u) throws UniversityFailure {
        this.university = u;
        validateData();
        if (currentState == States.SHOW_COURSES) {
            return showCouses();
        }
        if (currentState == States.SHOW_COURSE_DETAILS) {
            return showCourseDetails();
        }
        if (currentState == States.SHOW_ENROLLED_STUDENTS) {
            return showEnrolledStudents();
        }
        if (currentState == States.SHOW_LECTURES) {
            return showLectures();
        }
        currentState = States.SHOW_COURSES;
        return this;
    }

    private BasicController addNewCourse() {
        page.addNewCourseName(university.getTitle());
        String newCourseTitle = InputController.readString();
        String[] disciplineNames = Discipline.getNamesList();
        page.addNewCourseDiscipline(disciplineNames);
        int selectedDisciplineIndex = InputController.readInt();
        try {
            university.courses.addCource(newCourseTitle, Const.Discipline.values()[selectedDisciplineIndex]);
        } catch (UniversityWarning e) {
            page.error("Failed to add course", e.getMessage());
        }
        return this;
    }

    private BasicController addNewLecture() {
        page.addNewLectureTitle(selectedCourse.getName());
        String newLectureTitle = InputController.readString();
        String[] types = LectureType.getNamesList();
        page.addNewLectureDiscipline(types);
        int selectedType = InputController.readInt();
        if (selectedType < 0 || types.length <= selectedType) {
            page.error("Incorrect lecture type", "Lecture could not be added");
            return this;
        }
        LectureType newLectureType = LectureType.values()[selectedType];
        page.addNewLectureDescription();
        String newLectureDescription = InputController.readString();
        try {
            selectedCourse.addLecture(newLectureTitle, newLectureType, newLectureDescription);
        } catch (UniversityWarning e) {
            page.error("Failed to add lecture", e.getMessage());
        }
        return this;
    }

    private BasicController addScoreRecord() throws UniversityFailure {
        String[] students;
        students = selectedCourse.getEnrolledStudents().toNamesList();
        page.addScoreStudentSelection(students);
        int indexStudent = InputController.readInt() - 1;
        if (indexStudent < 0 || indexStudent >= students.length) {
            return this;
        }

        String[] lectures;
        lectures = selectedCourse.getLectures().toNamesList();
        page.addScoreLectureSelection(lectures);
        int indexLecture = InputController.readInt() - 1;
        if (indexLecture < 0 || indexLecture >= lectures.length) {
            return this;
        }
        page.addScoreReason();
        String reason = InputController.readString();
        page.addScore();
        int score = InputController.readInt();
        if (score < Const.MIN_SCORE || score > Const.MAX_SCORE) {
            page.addScoreErrorValue();
            return this;
        }
        currentState = States.SHOW_ENROLLED_STUDENTS;
        try {
            selectedCourse.getEnrolledStudents().get(indexStudent).addScore(
                    selectedCourse.getLectureByIndex(indexLecture),
                    score,
                    reason);
        } catch (UniversityWarning e) {
            page.error("Error: Unable to add score", e.getMessage());
            e.printStackTrace();
        }

        return this;
    }

    private void appointRoom() throws UniversityFailure {
        String[] buildings;
        try {
            buildings = university.buildings.getBuildings().toNamesList();
        } catch (UniversityFailure e1) {
            page.error("Unable to get buildings list", e1.getMessage());
            return;
        }
        page.appointRoomSelectBuilding(buildings);
        int indexBuilding = InputController.readInt();
        if (indexBuilding < 0 || indexBuilding >= university.buildings.countBuildings()) {
            page.show("Cancelled");
            return;
        }
        Building selectedBuilding;
        try {
            selectedBuilding = university.buildings.getBuildingByIndex(indexBuilding);
        } catch (UniversityWarning e1) {
            page.error("Unable to get selected building", e1.getMessage());
            return;
        }
        String[] rooms = {};
        try {
            rooms = selectedBuilding.getRooms().toNamesList();
        } catch (UniversityFailure e) {
            page.error("Failed to get rooms list", e.getMessage());
            return;
        }
        if (rooms.length < 1) {
            page.showWarning("Selected building has no rooms");
            return;
        }
        page.appointRoomSelectRoom(rooms);
        int indexRoom = InputController.readInt();
        Room selectedRoom;
        try {
            selectedRoom = selectedBuilding.getRoomByIndex(indexRoom);
        } catch (UniversityWarning e1) {
            page.error("Unable to get selected room", e1.getMessage());
            return;
        }
        if (indexRoom >= rooms.length) {
            page.show("Cancelled");
            return;
        }
        page.appointRoomAppointAll();
        if (yes()) {
            MyArray<Lecture> unappointedLectures = new MyArray<Lecture>(new Lecture[0]);
            MyArray<String> errors = new MyArray<String>(new String[0]);
            for (Lecture l : selectedCourse.getLectures()) {
                try {
                    l.setRoom(selectedRoom);
                } catch (UniversityWarning e) {
                    unappointedLectures.add(l);
                    errors.add(e.getMessage());
                }
            }
            if (unappointedLectures.size() > 0) {
                page.showStillUnassignedLectures(unappointedLectures.toNamesList(), errors.toArray());
            }
            readCommand();
        } else {
            try {
                selectedLecture.setRoom(selectedRoom);
            } catch (UniversityWarning e) {
                page.error("Failed to appoint room", e.getMessage());
            }
        }
    }

    private BasicController assignAnotherTeacher() throws UniversityFailure {
        page.selectTeacher(university.teachers.getTeachers().toNamesList());
        int selectedTeacherIndex = InputController.readInt();
        if (selectedTeacherIndex >= 0 && selectedTeacherIndex < university.teachers.getTeachers().size()) {
            try {
                selectedCourse.setTeacher(university.teachers.getTeacherByIndex(selectedTeacherIndex));
            } catch (UniversityWarning e) {
                page.error("Failed to assign teacher", e.getMessage());
            }
        }
        return this;
    }

    private BasicController deleteCourse() throws UniversityFailure {
        page.confirmDeleteCourse();
        if (yes()) {
            try {
                university.courses.removeCourse(selectedCourse);
            } catch (UniversityWarning e) {
                page.error("Failed to remove course", e.getMessage());
            }
        }
        currentState = States.SHOW_COURSES;
        return this;
    }

    private BasicController deleteLecture() {
        page.deleteLectureConfirmation(university.getTitle(), selectedCourse.getName(), selectedLecture.getName());
        if (yes()) {
            try {
                selectedCourse.removeLecture(selectedLecture);
            } catch (Exception e) {
                page.error("Failed to delete lecture", e.getMessage());
            }
        }
        return this;
    }

    private BasicController disenrollStudent(Student student) throws UniversityFailure {
        page.confirmDisenroll(selectedCourse.getName(), student.getName());
        if (yes()) {
            try {
                selectedCourse.disenrollStudent(student);
            } catch (UniversityWarning e) {
                page.error("Failed to disenroll student", e.getMessage());
            }
        }
        return this;
    }

    private BasicController editLecture() throws UniversityFailure {
        String[] menu = { "Back", "Update Description", "Appoint a room", "Delete lecture" };
        int updateDescription = 1;
        int appointRoom = 2;
        int delete = 3;
        page.lectureDetails(
                university.getTitle(),
                selectedCourse.getName(),
                selectedLecture.getName(),
                selectedLecture.getLectureType().getRepresentation(),
                selectedLecture.getDescription(),
                selectedLecture.getRoomName());
        page.lectureDetailsEditMenu(menu);
        readCommand();
        if (command == updateDescription) {
            updateDescription();
        }
        if (command == appointRoom) {
            appointRoom();
        }
        if (command == delete) {
            return deleteLecture();
        }
        return this;
    }

    private BasicController enrollAnotherStudent() throws UniversityFailure {
        MyArray<Student> allStudents = university.students.getStudents();
        page.enrollStudent(allStudents.toNamesList());
        readCommand();
        if (command < 0 || allStudents.size() <= command) {
            currentState = States.SHOW_COURSE_DETAILS;
            return this;
        }
        try {
            selectedCourse.enrollStudent(allStudents.get(command));
        } catch (UniversityWarning e) {
            page.error("Error: Failed to enroll student", e.getMessage());
        }
        // page.showError("Failed to add student",e.getMessage());
        currentState = States.SHOW_COURSE_DETAILS;
        return this;
    }

    private void readCommand() {
        command = InputController.readInt();
    }

    private BasicController showCourseDetails() throws UniversityFailure {
        String[] menu = {
                Messages.MENU_BACK,
                "Show enrolled students",
                "Show lectures",
                "Assign another teacher",
                "Delete this course"
        };
        page.courseDetails(
                university.getTitle(),
                selectedCourse.getName(),
                selectedCourse.getDisciplinePrint(),
                selectedCourse.getTeacherPrint()
                );
        page.courseDetailsMenu(menu);
        readCommand();
        switch (command) {
        case 1:
            currentState = States.SHOW_ENROLLED_STUDENTS;
            return this;
        case 2:
            currentState = States.SHOW_LECTURES;
            return this;
        case 3:
            return assignAnotherTeacher();
        case 4:
            return deleteCourse();
        default:
            return null;
        }
    }

    private BasicController showCouses() throws UniversityFailure {
        String[] coursesList;
        try {
            coursesList = university.courses.getCourses().toNamesList();
        } catch (UniversityFailure e1) {
            throw new UniversityFailure("Fatal Error: Internal courses data broken");
        }

        String[] menu = { GUI_COURSES_ADD_NEW_COURSE };
        page.coursesList(university.getTitle(),
                coursesList, menu);
        readCommand();
        if (command == 0 || command > coursesList.length + 1) {
            return null;
        }
        if (command == (coursesList.length + 1)) {
            return addNewCourse();
        }
        try {
            selectedCourse = university.courses.getCourseByIndex(command - 1);
            currentState = States.SHOW_COURSE_DETAILS;
        } catch (UniversityWarning e) {
            page.error("Failed to get course details", e.getMessage());
            currentState = States.SHOW_COURSES;
        }
        return this;
    }

    private BasicController showEnrolledStudents() throws UniversityFailure {
        String[] students;
        try {
            students = selectedCourse.getEnrolledStudents().toNamesList();
        } catch (UniversityFailure e) {
            page.error("Error: Failed to get list of enrolled students", e.getMessage());
            return this;
        }
        page.enrolledStudentsList(students);
        readCommand();
        if (command == students.length + 1) {
            return enrollAnotherStudent();
        }
        if (command == students.length + 2) {
            return addScoreRecord();
        }
        if (command == students.length + 3) {
            return showScores();
        }
        if (0 < command && command <= students.length) {
            Student student;
            try {
                student = selectedCourse.getEnrolledStudents().get(command - 1);
            } catch (UniversityFailure e) {
                page.error("Error: Failed to get student", e.getMessage());
                return this;
            }
            return disenrollStudent(student);
        }
        currentState = States.SHOW_COURSE_DETAILS;
        return this;
    }

    private BasicController showLectures() throws UniversityFailure {
        currentState = States.SHOW_COURSE_DETAILS;
        String[] lectureNames;
        lectureNames = selectedCourse.getLectures().toNamesList();
        page.lecturesList(
                university.getTitle(),
                selectedCourseTitle,
                lectureNames);
        int selectedAction = InputController.readInt();
        if (selectedAction == 0 || selectedAction > lectureNames.length + 1) {
            currentState = States.SHOW_COURSE_DETAILS;
            return this;
        }
        if (selectedAction == lectureNames.length + 1) {
            return addNewLecture();
        }
        try {
            selectedLecture = selectedCourse.getLectureByIndex(selectedAction - 1);
        } catch (UniversityWarning e) {
            page.error("Failed to get a lecture", e.getMessage());
            return this;
        }
        return editLecture();
    }

    private BasicController showScores() {
        String[][] scores = selectedCourse.getScoreTable();
        page.groupScoresTable(scores);
        readCommand();
        return this;
    }

    private void updateDescription() {
        page.addNewLectureDescription();
        String newLectureDescription = InputController.readString();
        selectedLecture.setDescription(newLectureDescription);
    }

    private void validateData() throws UniversityFailure {
        if (university == null || university.courses == null) {
            throw new UniversityFailure("FATAL ERROR: Courses internal storage is broken");
        }
    }
}
